/**
 * @file queue.hpp
 * @author 逆流 (1171267147@qq.com)
 * @brief
 * @version 0.1
 * @date 2024-10-15
 *
 * @copyright Copyright (c) 2024
 *
 */

#ifndef INC_STL_QUEUE_HPP_
#define INC_STL_QUEUE_HPP_

#include <initializer_list>
#include <stdexcept>

#include "node.h"

namespace stl {

template <typename T>
class Queue {
public:
    Queue() : head_(nullptr), tail_(nullptr), length_(0) {}
    Queue(const std::initializer_list<T>& list) : head_(nullptr), tail_(nullptr), length_(0) {
        for (const auto& val : list) {
            auto node = new SingleNode<T>(val);
            if (head_ == nullptr) {
                head_ = node;
            } else {
                tail_->next = node;
            }
            tail_ = node;
            length_++;
        }
    }

    ~Queue() { Clear(); }

    std::size_t length() const { return length_; }

    bool Empty() const { return nullptr == head_ || nullptr == tail_; }

    void Clear() {
        while (head_ != nullptr) {
            auto next = head_->next;
            delete head_;
            head_ = next;
        }
        tail_   = nullptr;
        length_ = 0;
    }

    void Enqueue(const T& data) {
        auto node = new SingleNode<T>(data);
        if (tail_ == nullptr) {
            head_ = node;
        } else {
            tail_->next = node;
        }
        tail_ = node;
        length_++;
    }

    T Front() const {
        if (Empty()) {
            throw std::out_of_range("queue is empty");
        }
        return head_->data;
    }

    T Dequeue() {
        if (Empty()) {
            throw std::out_of_range("queue is empty");
        }
        auto node = head_;
        head_     = head_->next;
        if (head_ == nullptr) {
            tail_ = nullptr;
        }
        T data = node->data;
        delete node;
        length_--;
        return data;
    }

private:
    SingleNode<T>* head_;
    SingleNode<T>* tail_;
    std::size_t    length_;
};
}  // namespace stl
#endif  // INC_STL_QUEUE_HPP_
